package com.encryption.plugin.util;

import com.encryption.plugin.domain.EncryptData;
import javassist.*;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;

/**
 * @author 刘志强
 * @title: AgentTransformer
 * @projectName class-encryption-plugin
 * @description: TODO
 * @date 2021/12/1911:27
 */
public class AgentTransformer implements ClassFileTransformer {

    private static final String certificateFile = "encryption.certificate";
    /**
     * 加载配置文件的类
     */
    private static String classPathResource = "org/springframework/core/io/ClassPathResource";
    /**
     * spring asm 读取字节码的类
     */
    private static String classReader = "org/springframework/asm/ClassReader";

    private EncryptData encryptData;

    /**
     * 构造方法
     */
    public AgentTransformer() {
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain domain, byte[] classBuffer) {

        // Log.info(className);

        String pwd = "12345678";
        // 配置文件解读插装
        if (classPathResource.equals(className)) {
            CtClass ctClass;
            try {
                //使用全称,用于取得字节码类
                ClassPool classPool = ClassPool.getDefault();
                ctClass = classPool.makeClass(new ByteArrayInputStream(classBuffer));
                CtMethod mt = ctClass.getDeclaredMethod("getInputStream");
                String javaCode = "String c=" + "\"" + pwd + "\"" + ";" + "is=com.encryption.plugin.util.JarDecryptor.getInstance().decryptConfigFile(this.path,is,c);";
                mt.insertAt(999, javaCode);
                return ctClass.toBytecode();
            } catch (Throwable e) {
                System.out.println("出错误了");
                e.printStackTrace();
            }
        }

        if (classReader.equals(className)) {
            try {
                System.out.println(className);
                ClassPool classPool = ClassPool.getDefault();
                classPool.appendClassPath(new LoaderClassPath(loader));
                CtClass ctClass = classPool.getCtClass("org.springframework.asm.ClassReader");
                CtClass ctClass1 = classPool.getCtClass("java.io.InputStream");
                CtConstructor ctConstructor = ctClass.getDeclaredConstructor(new CtClass[]{ctClass1});

                ctConstructor.setBody("this(com.encryption.plugin.util.ClassUtils.readStream($1, false," + "\"" + pwd + "\"" + "));");
                return ctClass.toBytecode();
            } catch (Throwable e) {
                System.out.println("出错误了");
                e.printStackTrace();
            }
        }

        if (className == null || domain == null || loader == null) {
            return classBuffer;
        }

        //获取类所在的项目运行路径
        String projectPath = domain.getCodeSource().getLocation().getPath();
        projectPath = JarUtils.getRootPath(projectPath);
        if (StrUtils.isEmpty(projectPath)) {
            return classBuffer;
        }
        className = className.replace("/", ".").replace("\\", ".");


        if (this.encryptData == null) {
            try {
                if (loader != null) {
                    InputStream inputStream = loader.getResourceAsStream(certificateFile);
                    if (inputStream != null) {
                        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
                        EncryptData encryptData = (EncryptData) objectInputStream.readObject();
                        this.encryptData = encryptData;
                    }
                }
            } catch (Throwable e) {
                Log.info("Certificate file detection exception:" + e);
                e.getStackTrace();
            }
        }

        if (this.encryptData == null) {
            return classBuffer;
        } else {

            Boolean on = true;
            for (String aPackage : this.encryptData.getPackages()) {
                if (className.contains(aPackage)) {
                    on = false;
                    break;
                }
            }
            if (on) {
                return classBuffer;
            } else {
                byte[] bytes = DESUtil.decryptByte(this.encryptData.getPassword(), classBuffer);
                if (bytes != null) {
                    return bytes;
                }
                return classBuffer;
            }
        }
    }
}
